package com.yzjs.common.export;

import com.yzjs.common.base.entity.AbstractBasePojo;
import com.yzjs.common.exception.RRException;
import com.yzjs.common.util.FileUtil;
import com.yzjs.common.util.conversion.DateUtils;
import com.yzjs.common.util.conversion.JsonUtil;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.usermodel.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

public abstract class AbstractExcelExport<T extends AbstractBasePojo> extends AbstractExport<T> {
    private int maxRowCount = 1000000;

    private String extendName = ".xlsx";

    public String export(Date exportTime, String baseDir, T param, IExportProgress progress) {
        String subDir = DateUtils.date2Str(exportTime, DateUtils.yyyyMMdd);
        String prefixName = UUID.randomUUID() + "_";
        String fileName = this.fileName;
        if (this.fileName.indexOf(".") > 0)
            fileName = this.fileName.substring(0, this.fileName.indexOf("."));
        this.logger.info("{}", baseDir + subDir + prefixName + fileName + "_*" + this.extendName);
        File dir = new File(baseDir + subDir);
        if (!dir.exists())
            dir.mkdirs();
        XSSFWorkbook wb = null;
        XSSFSheet sheet = null;
        XSSFRow row = null;
        XSSFCell cell = null;
        try {
            int pageNum = 1;
            int fileRowCount = 0;
            long exportRowCount = 0L;
            File outFile = null;
            List<File> outFiles = new ArrayList<>();
            param.setPageSize(Integer.valueOf(getPageSize()));
            List<?> dataList = null;
            Map<String, Object> valMap = null;
            Object val = null;
            while (true) {
                param.setPageNum(Integer.valueOf(pageNum));
                pageNum++;
                dataList = getDataList(param);
                if (dataList == null || dataList.isEmpty())
                    break;
                for (Object obj : dataList) {
                    if (fileRowCount % (this.maxRowCount + 1) == 0) {
                        if (wb != null) {
                            outFile = new File(baseDir + subDir + prefixName + fileName + "_" + (outFiles.size() + 1) + this.extendName);
                            outFiles.add(outFile);
                            OutputStream out = new FileOutputStream(outFile);
                            wb.write(out);
                            fileRowCount = 0;
                            wb.close();
                            out.close();
                        }
                        wb = new XSSFWorkbook();
                        sheet = wb.createSheet(fileName);
                        XSSFCellStyle headerStyle = wb.createCellStyle();
                        headerStyle.setAlignment(HorizontalAlignment.CENTER);
                        XSSFFont headerFont = wb.createFont();
                        headerFont.setBold(true);
                        headerStyle.setFont((Font)headerFont);
                        row = sheet.createRow(fileRowCount);
                        fileRowCount++;
                        for (int j = 0; j < this.headerNames.length; j++) {
                            sheet.setColumnWidth(j, (getStrLength(this.headerNames[j]) + 4) * 256);
                            cell = row.createCell(j);
                            cell.setCellStyle((CellStyle)headerStyle);
                            cell.setCellValue(this.headerNames[j]);
                        }
                    }


                    valMap =  JsonUtil.objectToMap(obj)  ;
                    row = sheet.createRow(fileRowCount);
                    fileRowCount++;
                    for (int i = 0; i < this.colNames.length; i++) {
                        val = getObjVal(this.colNames[i], obj, valMap);
                        val = (val == null) ? "" : val;
                        if (fileRowCount < 10 && sheet.getColumnWidth(i) < getStrLength(val.toString()) * 256)
                            sheet.setColumnWidth(i, getStrLength(val.toString()) * 256);
                        cell = row.createCell(i);
                        if (val instanceof Date) {
                            cell.setCellValue(DateUtils.date2Str((Date)val,  DateUtils.datetimeFormat ));
                            cell.setCellType(CellType.STRING);
                        } else if (val instanceof Double) {
                            cell.setCellValue(((Double)val).doubleValue());
                            cell.setCellType(CellType.NUMERIC);
                        } else if (val instanceof Integer) {
                            cell.setCellValue(((Integer)val).intValue());
                            cell.setCellType(CellType.NUMERIC);
                        } else if (val instanceof Float) {
                            cell.setCellValue(((Float)val).floatValue());
                            cell.setCellType(CellType.NUMERIC);
                        } else {
                            cell.setCellValue(val.toString());
                            cell.setCellType(CellType.STRING);
                        }
                    }
                    exportRowCount++;
                    if (exportRowCount % getProgressRowCount() == 0L) {
                        this.logger.info("{}/{}{}", new Object[] { Long.valueOf(exportRowCount), param.getTotalRowCount(), baseDir + subDir + prefixName + fileName + "_*" + this.extendName });
                        if (progress != null)
                            progress.doProgress(param.getTotalRowCount().longValue(), exportRowCount);
                    }
                }
            }
            if (wb != null) {
                outFile = new File(baseDir + subDir + prefixName + fileName + "_" + (outFiles.size() + 1) + this.extendName);
                outFiles.add(outFile);
                OutputStream out = new FileOutputStream(outFile);
                wb.write(out);
                fileRowCount = 0;
                wb.close();
                out.close();
            }
            if (progress != null && param.getTotalRowCount().longValue() > 0L)
                progress.doProgress(param.getTotalRowCount().longValue(), exportRowCount);
            String returnPath = subDir + prefixName + fileName + ".zip";
            if (outFiles.size() > 0) {
                FileOutputStream outputStream = new FileOutputStream(baseDir + returnPath);
                 FileUtil.toZip(outFiles, outputStream, true);
            }
            this.logger.info("{}{}", Integer.valueOf(outFiles.size()), baseDir + returnPath);
            return returnPath;
        } catch (Exception ex) {
            throw new RRException("导出失败");
        } finally {
            try {
                if (wb != null)
                    wb.close();
            } catch (IOException iOException) {}
        }
    }

    public void exportFile(Date exportTime, String baseDir, T param, IExportProgress progress) {
        String fileName = this.fileName;
        if (this.fileName.indexOf(".") > 0)
            fileName = this.fileName.substring(0, this.fileName.indexOf("."));
        this.logger.info("{}", baseDir + fileName + this.extendName);
        XSSFWorkbook wb = null;
        XSSFSheet sheet = null;
        XSSFRow row = null;
        XSSFCell cell = null;
        try {
            int fileRowCount = 0;
            long exportRowCount = 0L;
            File outFile = null;
            List<File> outFiles = new ArrayList<>();
            List<?> dataList = null;
            Map<String, Object> valMap = null;
            Object val = null;
            dataList = getDataList(param);
            for (Object obj : dataList) {
                if (fileRowCount % (this.maxRowCount + 1) == 0) {
                    if (wb != null) {
                        outFile = new File(baseDir + fileName + this.extendName);
                        outFiles.add(outFile);
                        OutputStream out = new FileOutputStream(outFile);
                        wb.write(out);
                        fileRowCount = 0;
                        wb.close();
                        out.close();
                    }
                    wb = new XSSFWorkbook();
                    sheet = wb.createSheet(fileName);
                    XSSFCellStyle headerStyle = wb.createCellStyle();
                    headerStyle.setAlignment(HorizontalAlignment.CENTER);
                    XSSFFont headerFont = wb.createFont();
                    headerFont.setBold(true);
                    headerStyle.setFont((Font)headerFont);
                    row = sheet.createRow(fileRowCount);
                    fileRowCount++;
                    for (int j = 0; j < this.headerNames.length; j++) {
                        sheet.setColumnWidth(j, (getStrLength(this.headerNames[j]) + 4) * 256);
                        cell = row.createCell(j);
                        cell.setCellStyle((CellStyle)headerStyle);
                        cell.setCellValue(this.headerNames[j]);
                    }
                }
                valMap =  JsonUtil.objectToMap(obj)  ;
                row = sheet.createRow(fileRowCount);
                fileRowCount++;
                for (int i = 0; i < this.colNames.length; i++) {
                    val = getObjVal(this.colNames[i], obj, valMap);
                    val = (val == null) ? "" : val;
                    if (fileRowCount < 10 && sheet.getColumnWidth(i) < getStrLength(val.toString()) * 256)
                        sheet.setColumnWidth(i, getStrLength(val.toString()) * 256);
                    cell = row.createCell(i);
                    if (val instanceof Date) {
                        cell.setCellValue(DateUtils.date2Str((Date)val,  DateUtils.datetimeFormat ) );
                        cell.setCellType(CellType.STRING);
                    } else if (val instanceof Double) {
                        cell.setCellValue(((Double)val).doubleValue());
                        cell.setCellType(CellType.NUMERIC);
                    } else if (val instanceof Integer) {
                        cell.setCellValue(((Integer)val).intValue());
                        cell.setCellType(CellType.NUMERIC);
                    } else if (val instanceof Float) {
                        cell.setCellValue(((Float)val).floatValue());
                        cell.setCellType(CellType.NUMERIC);
                    } else {
                        cell.setCellValue(val.toString());
                        cell.setCellType(CellType.STRING);
                    }
                }
                exportRowCount++;
                if (exportRowCount % getProgressRowCount() == 0L) {
                    this.logger.info("{}/{}{}", new Object[] { Long.valueOf(exportRowCount), param.getTotalRowCount(), baseDir + fileName + this.extendName });
                    if (progress != null)
                        progress.doProgress(param.getTotalRowCount().longValue(), exportRowCount);
                }
            }
            if (wb != null) {
                outFile = new File(baseDir + fileName + this.extendName);
                outFiles.add(outFile);
                OutputStream out = new FileOutputStream(outFile);
                wb.write(out);
                fileRowCount = 0;
                wb.close();
                out.close();
            }
            if (progress != null)
                progress.doProgress(param.getTotalRowCount().longValue(), exportRowCount);
        } catch (Exception ex) {
            throw new RRException("导出失败");
        } finally {
            try {
                if (wb != null)
                    wb.close();
            } catch (IOException iOException) {}
        }
    }

    private int getStrLength(String s) {
        int length = 0;
        for (int i = 0; i < s.length(); i++) {
            int ascii = Character.codePointAt(s, i);
            length += (ascii >= 0 && ascii <= 255) ? 1 : 2;
        }
        return length;
    }
}
